package com.clp.protocol.core.async;

import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import io.netty.util.concurrent.Promise;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 成功策略
 */
public enum NSuccessPolicy {
    /**
     * 全部成功才是成功
     */
    ALL_IS_SUCCESS() {
        @Override
        public boolean apply(List<Boolean> isTrues) {
            for (boolean isTrue : isTrues) {
                if (!isTrue) return false;
            }
            return true;
        }

        @Override
        @SuppressWarnings({"rawtypes", "unchecked"})
        public <P extends IPromise, F extends IFuture> void apply(P promise, List<F> futures) {
            int futuresSize = futures.size();

            if (futuresSize == 0) throw new IllegalArgumentException();
            if (promise.isDone()) return;

            final AtomicBoolean[] isFinisheds = new AtomicBoolean[futuresSize];
            for (int i = 0; i < isFinisheds.length; i++) {
                isFinisheds[i] = new AtomicBoolean(false);
            }
            final AtomicBoolean[] isFaileds = new AtomicBoolean[futuresSize];
            for (int i = 0; i < isFaileds.length; i++) {
                isFaileds[i] = new AtomicBoolean(false);
            }

            Object lock = new Object();

            Class futureListenerClass = futures.get(0).getListenerClass();
            for (int index = 0; index < futuresSize; index++) {
                final int i = index;
                IFutureListener listener = (IFutureListener) Proxy.newProxyInstance(futureListenerClass.getClassLoader(), new Class[]{futureListenerClass}, new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if (method.getName().equals(IFutureListener.getOperationCompleteMethod().getName())) {
                            synchronized (lock) {
                                if (futures.get(i).isSuccess()) {
                                    isFaileds[i].set(false);

                                    // 检查是否是最后一个
                                    boolean isLastFinished = true;
                                    // 轮询检查所有其他结束的是否都成功
                                    boolean isAllFinishedSuccess = true;
                                    for (int j = 0; j < futuresSize; j++) {
                                        if (j == i) continue;
                                        if (isFinisheds[j].get()) {
                                            if (isFaileds[j].get()) {
                                                isAllFinishedSuccess = false;
                                            }
                                        } else {
                                            isLastFinished = false;
                                        }
                                    }

                                    // 设置结果
                                    if (isLastFinished) {
                                        if (isAllFinishedSuccess) {
                                            promise.setSuccess();
                                        } else {
                                            promise.setFailure(new RuntimeException("Future列表未能全部执行成功！"));
                                        }
                                    }
                                } else {
                                    isFaileds[i].set(true);

                                    // 检查是否是最后一个
                                    boolean isLastFinished = true;
                                    // 轮询检查是否全部执行失败
                                    boolean isAllFinishedFailed = true;
                                    for (int j = 0; j < futuresSize; j++) {
                                        if (j == i) continue;
                                        if (isFinisheds[j].get()) {
                                            if (!isFaileds[j].get()) {
                                                isAllFinishedFailed = false;
                                            }
                                        } else {
                                            isLastFinished = false;
                                        }
                                    }

                                    // 设置结果
                                    if (isLastFinished) {
                                        if (isAllFinishedFailed) {
                                            promise.setFailure(new RuntimeException("Future列表全部执行失败！"));
                                        } else {
                                            promise.setFailure(new RuntimeException("Future列表未能全部执行成功！"));
                                        }
                                    }
                                }
                                isFinisheds[i].set(true);
                            }
                            return null;
                        } else {
                            return method.invoke(args);
                        }
                    }
                });
                futures.get(i).addListener(listener);
            }
        }

        @Override
        @SuppressWarnings({"rawtypes", "unchecked"})
        public <P extends Promise<V>, V, F extends Future> void apply2(P promise, List<F> futures) {
            int futuresSize = futures.size();

            if (futuresSize == 0) throw new IllegalArgumentException();
            if (promise.isDone()) return;

            final AtomicBoolean[] isFinisheds = new AtomicBoolean[futuresSize];
            for (int i = 0; i < isFinisheds.length; i++) {
                isFinisheds[i] = new AtomicBoolean(false);
            }
            final AtomicBoolean[] isFaileds = new AtomicBoolean[futuresSize];
            for (int i = 0; i < isFaileds.length; i++) {
                isFaileds[i] = new AtomicBoolean(false);
            }

            Object lock = new Object();

            for (int index = 0; index < futuresSize; index++) {
                final int i = index;
                futures.get(i).addListener(new GenericFutureListener<Future<?>>() {
                    @Override
                    public void operationComplete(Future<?> future) throws Exception {
                        synchronized (lock) {
                            if (futures.get(i).isSuccess()) {
                                isFaileds[i].set(false);

                                // 检查是否是最后一个
                                boolean isLastFinished = true;
                                // 轮询检查所有其他结束的是否都成功
                                boolean isAllFinishedSuccess = true;
                                for (int j = 0; j < futuresSize; j++) {
                                    if (j == i) continue;
                                    if (isFinisheds[j].get()) {
                                        if (isFaileds[j].get()) {
                                            isAllFinishedSuccess = false;
                                        }
                                    } else {
                                        isLastFinished = false;
                                    }
                                }

                                // 设置结果
                                if (isLastFinished) {
                                    if (isAllFinishedSuccess) {
                                        promise.setSuccess(null);
                                    } else {
                                        promise.setFailure(new RuntimeException("Future列表未能全部执行成功！"));
                                    }
                                }
                            } else {
                                isFaileds[i].set(true);

                                // 检查是否是最后一个
                                boolean isLastFinished = true;
                                // 轮询检查是否全部执行失败
                                boolean isAllFinishedFailed = true;
                                for (int j = 0; j < futuresSize; j++) {
                                    if (j == i) continue;
                                    if (isFinisheds[j].get()) {
                                        if (!isFaileds[j].get()) {
                                            isAllFinishedFailed = false;
                                        }
                                    } else {
                                        isLastFinished = false;
                                    }
                                }

                                // 设置结果
                                if (isLastFinished) {
                                    if (isAllFinishedFailed) {
                                        promise.setFailure(new RuntimeException("Future列表全部执行失败！"));
                                    } else {
                                        promise.setFailure(new RuntimeException("Future列表未能全部执行成功！"));
                                    }
                                }
                            }
                            isFinisheds[i].set(true);
                        }
                    }
                });
            }
        }

        public <P extends IPromise<?, ?>> void apply3(P promise, List<ChannelFuture> futures) {
            int futuresSize = futures.size();

            if (futuresSize == 0) throw new IllegalArgumentException();
            if (promise.isDone()) return;

            final AtomicBoolean[] isFinisheds = new AtomicBoolean[futuresSize];
            for (int i = 0; i < isFinisheds.length; i++) {
                isFinisheds[i] = new AtomicBoolean(false);
            }
            final AtomicBoolean[] isFaileds = new AtomicBoolean[futuresSize];
            for (int i = 0; i < isFaileds.length; i++) {
                isFaileds[i] = new AtomicBoolean(false);
            }

            Object lock = new Object();

            for (int index = 0; index < futuresSize; index++) {
                final int i = index;
                futures.get(i).addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        synchronized (lock) {
                            if (futures.get(i).isSuccess()) {
                                isFaileds[i].set(false);

                                // 检查是否是最后一个
                                boolean isLastFinished = true;
                                // 轮询检查所有其他结束的是否都成功
                                boolean isAllFinishedSuccess = true;
                                for (int j = 0; j < futuresSize; j++) {
                                    if (j == i) continue;
                                    if (isFinisheds[j].get()) {
                                        if (isFaileds[j].get()) {
                                            isAllFinishedSuccess = false;
                                        }
                                    } else {
                                        isLastFinished = false;
                                    }
                                }

                                // 设置结果
                                if (isLastFinished) {
                                    if (isAllFinishedSuccess) {
                                        promise.setSuccess();
                                    } else {
                                        promise.setFailure(new RuntimeException("ChannelFuture列表未能全部执行成功！"));
                                    }
                                }
                            } else {
                                isFaileds[i].set(true);

                                // 检查是否是最后一个
                                boolean isLastFinished = true;
                                // 轮询检查是否全部执行失败
                                boolean isAllFinishedFailed = true;
                                for (int j = 0; j < futuresSize; j++) {
                                    if (j == i) continue;
                                    if (isFinisheds[j].get()) {
                                        if (!isFaileds[j].get()) {
                                            isAllFinishedFailed = false;
                                        }
                                    } else {
                                        isLastFinished = false;
                                    }
                                }

                                // 设置结果
                                if (isLastFinished) {
                                    if (isAllFinishedFailed) {
                                        promise.setFailure(new RuntimeException("ChannelFuture列表全部执行失败！"));
                                    } else {
                                        promise.setFailure(new RuntimeException("ChannelFuture列表未能全部执行成功！"));
                                    }
                                }
                            }
                            isFinisheds[i].set(true);
                        }
                    }
                });
            }
        }

        @Override
        @SuppressWarnings({"rawtypes", "unchecked"})
        public <P extends Promise<V>, V, F extends IFuture> void apply4(P promise, List<F> futures) {
            int futuresSize = futures.size();

            if (futuresSize == 0) throw new IllegalArgumentException();
            if (promise.isDone()) return;

            final AtomicBoolean[] isFinisheds = new AtomicBoolean[futuresSize];
            for (int i = 0; i < isFinisheds.length; i++) {
                isFinisheds[i] = new AtomicBoolean(false);
            }
            final AtomicBoolean[] isFaileds = new AtomicBoolean[futuresSize];
            for (int i = 0; i < isFaileds.length; i++) {
                isFaileds[i] = new AtomicBoolean(false);
            }

            Object lock = new Object();

            Class futureListenerClass = futures.get(0).getListenerClass();
            for (int index = 0; index < futuresSize; index++) {
                final int i = index;
                IFutureListener listener = (IFutureListener) Proxy.newProxyInstance(futureListenerClass.getClassLoader(), new Class[]{futureListenerClass}, new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if (method.getName().equals(IFutureListener.getOperationCompleteMethod().getName())) {
                            synchronized (lock) {
                                if (futures.get(i).isSuccess()) {
                                    isFaileds[i].set(false);

                                    // 检查是否是最后一个
                                    boolean isLastFinished = true;
                                    // 轮询检查所有其他结束的是否都成功
                                    boolean isAllFinishedSuccess = true;
                                    for (int j = 0; j < futuresSize; j++) {
                                        if (j == i) continue;
                                        if (isFinisheds[j].get()) {
                                            if (isFaileds[j].get()) {
                                                isAllFinishedSuccess = false;
                                            }
                                        } else {
                                            isLastFinished = false;
                                        }
                                    }

                                    // 设置结果
                                    if (isLastFinished) {
                                        if (isAllFinishedSuccess) {
                                            promise.setSuccess(null);
                                        } else {
                                            promise.setFailure(new RuntimeException("Future列表未能全部执行成功！"));
                                        }
                                    }
                                } else {
                                    isFaileds[i].set(true);

                                    // 检查是否是最后一个
                                    boolean isLastFinished = true;
                                    // 轮询检查是否全部执行失败
                                    boolean isAllFinishedFailed = true;
                                    for (int j = 0; j < futuresSize; j++) {
                                        if (j == i) continue;
                                        if (isFinisheds[j].get()) {
                                            if (!isFaileds[j].get()) {
                                                isAllFinishedFailed = false;
                                            }
                                        } else {
                                            isLastFinished = false;
                                        }
                                    }

                                    // 设置结果
                                    if (isLastFinished) {
                                        if (isAllFinishedFailed) {
                                            promise.setFailure(new RuntimeException("Future列表全部执行失败！"));
                                        } else {
                                            promise.setFailure(new RuntimeException("Future列表未能全部执行成功！"));
                                        }
                                    }
                                }
                                isFinisheds[i].set(true);
                            }
                            return null;
                        } else {
                            return method.invoke(args);
                        }
                    }
                });
                futures.get(i).addListener(listener);
            }
        }
    },

    /**
     * 有一个成功就是成功
     */
    ANY_IS_SUCCESS() {
        @Override
        public boolean apply(List<Boolean> isTrues) {
            for (boolean isTrue : isTrues) {
                if (isTrue) return true;
            }
            return false;
        }

        @Override
        @SuppressWarnings({"rawtypes", "unchecked"})
        public <P extends IPromise, F extends IFuture> void apply(P promise, List<F> futures) {
            int futuresSize = futures.size();

            if (futuresSize == 0) throw new IllegalArgumentException();
            if (promise.isDone()) return;

            final AtomicBoolean[] isFinisheds = new AtomicBoolean[futuresSize];
            for (int i = 0; i < isFinisheds.length; i++) {
                isFinisheds[i] = new AtomicBoolean(false);
            }
            final AtomicBoolean[] isFaileds = new AtomicBoolean[futuresSize];
            for (int i = 0; i < isFaileds.length; i++) {
                isFaileds[i] = new AtomicBoolean(false);
            }

            Object lock = new Object();

            Class futureListenerClass = futures.get(0).getListenerClass();
            for (int index = 0; index < futuresSize; index++) {
                final int i = index;
                IFutureListener listener = (IFutureListener) Proxy.newProxyInstance(futures.get(0).getClass().getClassLoader(), new Class[]{futureListenerClass}, new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if (method.getName().equals(IFutureListener.getOperationCompleteMethod().getName())) {
                            synchronized (lock) {
                                if (futures.get(i).isSuccess()) {
                                    isFaileds[i].set(false);
                                    if (!promise.isDone()) promise.setSuccess();
                                } else {
                                    isFaileds[i].set(true);

                                    // 检查是否是最后一个
                                    boolean isLastFinished = true;
                                    // 轮询检查是否全部执行失败
                                    boolean isAllFinishedFailed = true;
                                    for (int j = 0; j < futuresSize; j++) {
                                        if (j == i) continue;
                                        if (isFinisheds[j].get()) {
                                            if (!isFaileds[j].get()) {
                                                isAllFinishedFailed = false;
                                            }
                                        } else {
                                            isLastFinished = false;
                                        }
                                    }

                                    if (isLastFinished && isAllFinishedFailed) {
                                        promise.setFailure(new RuntimeException("Future列表全部执行失败！"));
                                    }
                                }
                                isFinisheds[i].set(true);
                            }
                            return null;
                        } else {
                            return method.invoke(args);
                        }
                    }
                });
                futures.get(i).addListener(listener);
            }
        }

        @Override
        @SuppressWarnings({"rawtypes", "unchecked"})
        public <P extends Promise<V>, V, F extends Future> void apply2(P promise, List<F> futures) {
            int futuresSize = futures.size();

            if (futuresSize == 0) throw new IllegalArgumentException();
            if (promise.isDone()) return;

            final AtomicBoolean[] isFinisheds = new AtomicBoolean[futuresSize];
            for (int i = 0; i < isFinisheds.length; i++) {
                isFinisheds[i] = new AtomicBoolean(false);
            }
            final AtomicBoolean[] isFaileds = new AtomicBoolean[futuresSize];
            for (int i = 0; i < isFaileds.length; i++) {
                isFaileds[i] = new AtomicBoolean(false);
            }

            Object lock = new Object();

            for (int index = 0; index < futuresSize; index++) {
                final int i = index;
                futures.get(i).addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        synchronized (lock) {
                            if (futures.get(i).isSuccess()) {
                                isFaileds[i].set(false);
                                if (!promise.isDone()) promise.setSuccess(null);
                            } else {
                                isFaileds[i].set(true);

                                // 检查是否是最后一个
                                boolean isLastFinished = true;
                                // 轮询检查是否全部执行失败
                                boolean isAllFinishedFailed = true;
                                for (int j = 0; j < futuresSize; j++) {
                                    if (j == i) continue;
                                    if (isFinisheds[j].get()) {
                                        if (!isFaileds[j].get()) {
                                            isAllFinishedFailed = false;
                                        }
                                    } else {
                                        isLastFinished = false;
                                    }
                                }

                                if (isLastFinished && isAllFinishedFailed) {
                                    promise.setFailure(new RuntimeException("ChannelFuture列表全部执行失败！"));
                                }
                            }
                            isFinisheds[i].set(true);
                        }
                    }
                });
            }
        }

        public <P extends IPromise<?, ?>> void apply3(P promise, List<ChannelFuture> futures) {
            int futuresSize = futures.size();

            if (futuresSize == 0) throw new IllegalArgumentException();
            if (promise.isDone()) return;

            final AtomicBoolean[] isFinisheds = new AtomicBoolean[futuresSize];
            for (int i = 0; i < isFinisheds.length; i++) {
                isFinisheds[i] = new AtomicBoolean(false);
            }
            final AtomicBoolean[] isFaileds = new AtomicBoolean[futuresSize];
            for (int i = 0; i < isFaileds.length; i++) {
                isFaileds[i] = new AtomicBoolean(false);
            }

            Object lock = new Object();

            for (int index = 0; index < futuresSize; index++) {
                final int i = index;
                futures.get(i).addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        synchronized (lock) {
                            if (futures.get(i).isSuccess()) {
                                isFaileds[i].set(false);
                                if (!promise.isDone()) promise.setSuccess();
                            } else {
                                isFaileds[i].set(true);

                                // 检查是否是最后一个
                                boolean isLastFinished = true;
                                // 轮询检查是否全部执行失败
                                boolean isAllFinishedFailed = true;
                                for (int j = 0; j < futuresSize; j++) {
                                    if (j == i) continue;
                                    if (isFinisheds[j].get()) {
                                        if (!isFaileds[j].get()) {
                                            isAllFinishedFailed = false;
                                        }
                                    } else {
                                        isLastFinished = false;
                                    }
                                }

                                if (isLastFinished && isAllFinishedFailed) {
                                    promise.setFailure(new RuntimeException("ChannelFuture列表全部执行失败！"));
                                }
                            }
                            isFinisheds[i].set(true);
                        }
                    }
                });
            }
        }

        @Override
        @SuppressWarnings({"rawtypes", "unchecked"})
        public <P extends Promise<V>, V, F extends IFuture> void apply4(P promise, List<F> futures) {
            int futuresSize = futures.size();

            if (futuresSize == 0) throw new IllegalArgumentException();
            if (promise.isDone()) return;

            final AtomicBoolean[] isFinisheds = new AtomicBoolean[futuresSize];
            for (int i = 0; i < isFinisheds.length; i++) {
                isFinisheds[i] = new AtomicBoolean(false);
            }
            final AtomicBoolean[] isFaileds = new AtomicBoolean[futuresSize];
            for (int i = 0; i < isFaileds.length; i++) {
                isFaileds[i] = new AtomicBoolean(false);
            }

            Object lock = new Object();

            Class futureListenerClass = futures.get(0).getListenerClass();
            for (int index = 0; index < futuresSize; index++) {
                final int i = index;
                IFutureListener listener = (IFutureListener) Proxy.newProxyInstance(futures.get(0).getClass().getClassLoader(), new Class[]{futureListenerClass}, new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if (method.getName().equals(IFutureListener.getOperationCompleteMethod().getName())) {
                            synchronized (lock) {
                                if (futures.get(i).isSuccess()) {
                                    isFaileds[i].set(false);
                                    if (!promise.isDone()) promise.setSuccess(null);
                                } else {
                                    isFaileds[i].set(true);

                                    // 检查是否是最后一个
                                    boolean isLastFinished = true;
                                    // 轮询检查是否全部执行失败
                                    boolean isAllFinishedFailed = true;
                                    for (int j = 0; j < futuresSize; j++) {
                                        if (j == i) continue;
                                        if (isFinisheds[j].get()) {
                                            if (!isFaileds[j].get()) {
                                                isAllFinishedFailed = false;
                                            }
                                        } else {
                                            isLastFinished = false;
                                        }
                                    }

                                    if (isLastFinished && isAllFinishedFailed) {
                                        promise.setFailure(new RuntimeException("Future列表全部执行失败！"));
                                    }
                                }
                                isFinisheds[i].set(true);
                            }
                            return null;
                        } else {
                            return method.invoke(args);
                        }
                    }
                });
                futures.get(i).addListener(listener);
            }
        }
    };

    // ----------------------------------------------------------------------------------------------

    public <P extends IPromise<?, ?>, F extends IFuture<?, ?>> void apply(P promise, F... futures) {
        apply(promise, Arrays.asList(futures));
    }

    @SuppressWarnings("rawtypes")
    public abstract <P extends IPromise, F extends IFuture> void apply(P promise, List<F> futures);

    // ----------------------------------------------------------------------------------------------
    @SuppressWarnings("rawtypes")
    public <P extends Promise<V>, V, F extends Future> void apply2(P promise, F... futures) {
        apply2(promise, Arrays.asList(futures));
    }

    @SuppressWarnings("rawtypes")
    public abstract  <P extends Promise<V>, V, F extends Future> void apply2(P promise, List<F> futures);

    // ----------------------------------------------------------------------------------------------
    public <P extends IPromise<?, ?>> void apply3(P promise, ChannelFuture... futures) {
        apply3(promise, Arrays.asList(futures));
    }

    public abstract <P extends IPromise<?, ?>> void apply3(P promise, List<ChannelFuture> futures);

    // ----------------------------------------------------------------------------------------------

    @SuppressWarnings("rawtypes")
    public <P extends Promise<V>, V, F extends IFuture> void apply4(P promise, F... futures) {
        apply4(promise, Arrays.asList(futures));
    }

    @SuppressWarnings("rawtypes")
    public abstract <P extends Promise<V>, V, F extends IFuture> void apply4(P promise, List<F> futures);

    // ----------------------------------------------------------------------------------------------

    public abstract boolean apply(List<Boolean> isTrues);

}
